#include "Window.h"
#include <string>
#include <iostream>
#include <stdexcept>

Window::Window(const std::wstring name, DWORD style, DWORD styleEx):
	name(name),
	x(500), y(500),
	width(200),
	height(200),
	style(style),
	styleEx(styleEx),
	registered(false){
}

Window::~Window() {
	windows.erase(hwnd);
	dispose();
}

void Window::host() {
	MSG msg;
	while(GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
}

void Window::create() {
	currInit = this;
	registerClass();
	hwnd = CreateWindowEx(
		styleEx,
		className.c_str(),
		name.c_str(),
		style,
		x,
		y,
		width,
		height,
		parent,
		NULL,
		hInstance,
		NULL);
	if(hwnd == NULL){
		MessageBox(NULL, (std::wstring(L"Unable to create window: ") + std::to_wstring(GetLastError())).c_str(), NULL, MB_OK);
		exit(5);
	}
	currInit = NULL;
}

void Window::dispose() {
	if(isCreated()) {
		DestroyWindow(hwnd);
		unregisterClass();
		hwnd = NULL;
	}
}

void Window::setVisible(bool visible) {
	if(hwnd == NULL) {
		create();
	}
	ShowWindow(hwnd, (visible?SW_SHOW:SW_HIDE));
}

void Window::setSize(int width, int height) {
	this->width = width;
	this->height = height;
	if(isCreated()) {
		MoveWindow(hwnd, x, y, width, height, true);
	}
}

bool Window::isCreated() {
	return hwnd != NULL;
}

//void Window::addListener(UINT msg, std::function<void(void)> listener) {
//	listeners[msg].push_back(listener);
//}

const HINSTANCE Window::hInstance = GetModuleHandle(NULL);

bool Window::registerClass() {
	WNDCLASSEX wndClass;
	wndClass.cbSize        = sizeof(WNDCLASSEX);
	wndClass.style         = 0;
	wndClass.lpfnWndProc   = WndProc;
	wndClass.cbClsExtra    = 0;
	wndClass.cbWndExtra    = 0;
	wndClass.hInstance     = hInstance;
	wndClass.hIcon         = NULL;
	wndClass.hCursor       = NULL;
	wndClass.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
	wndClass.lpszMenuName  = NULL;
	wndClass.lpszClassName = className.c_str();
	wndClass.hIconSm       = NULL;

	ATOM classAtom = RegisterClassEx(&wndClass);
	if(classAtom == 0) {
		MessageBox(NULL, (std::wstring(L"Unable to register window class: ") + std::to_wstring(GetLastError()) + name).c_str(), NULL, MB_OK);
		return false;
	} else {
		registered = true;
		return true;
	}
}

bool Window::unregisterClass() {
	UnregisterClass(className.c_str(), hInstance);
	return true;
}

void Window::setName(const std::wstring name) {
	this->name = name;
	if(isCreated()) {
		SetWindowText(hwnd, name.c_str());
	}
}

const std::wstring& Window::getName() const {
	return name;
}

void Window::setClassName(const std::wstring className) {
	if(!isRegistered()) {
		this->className = className;
	}
}

const std::wstring& Window::getClassName() const {
	return className;
}

void Window::setStyle(DWORD style) {
	this->style = style;
	if(isCreated()) {
		SetWindowLong(hwnd, GWL_STYLE, style);
	}
}

void Window::setExtendedStyle(DWORD styleEx) {
	this->styleEx = styleEx;
	if(isCreated()) {
		SetWindowLong(hwnd, GWL_EXSTYLE, styleEx);
	}
}

bool Window::isRegistered() {
	return registered;
}

LRESULT Window::processMessage(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	/*try {
		for(auto listener : listeners.at(msg)) {
			listener();
		} 
	} catch(std::out_of_range) {

	}*/
	return DefWindowProc(hwnd, msg, wParam, lParam);
}

std::unordered_map<HWND, Window*> Window::windows;
Window* Window::currInit;

LRESULT CALLBACK Window::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
	try {
		return windows.at(hwnd)->processMessage(hwnd, msg, wParam, lParam);
	} catch(std::out_of_range ex) {
		if(currInit != NULL) {
			windows.insert(std::make_pair(hwnd, currInit));
			return currInit->processMessage(hwnd, msg, wParam, lParam);
			currInit = NULL;
		} else {
			return DefWindowProc(hwnd, msg, wParam, lParam);
		}
	}
}